#include <vector>
#include <boost/bind.hpp>
#include "Connection.h"
#include "Trace.h"
#include "RequestHandler.h"
#include "Message.h"

namespace tsc
{
namespace common
{

unsigned int Connection::sInstanceCounter = 0x00100000;

/*******************************************************************************
 *
 *
 *
 ******************************************************************************/
Connection::Connection(boost::asio::io_service& service, RequestHandler& handler)
  : mSocket(service),
    mRequestHandler(handler),
    mInstance(++sInstanceCounter)
{
  ENTER("Connection::Connection");

  mDescribeStr = STR("Connection instanceId=0x%08X.",
                     sInstanceCounter);

  TRACE(3, STR("%s Initiated. Waiting for client.",
               mDescribeStr.data()));

  RETURN;
} // Connection::Connection

/*******************************************************************************
 *
 *
 *
 ******************************************************************************/
boost::asio::ip::tcp::socket& Connection::socket()
{
  return mSocket;
} // Connection::socket

/*******************************************************************************
 *
 *
 *
 ******************************************************************************/
void Connection::start()
{
  ENTER("Connection::start");

  TRACE(3, STR("%s Client connected form address=%s:%u. Waiting for request data.",
               mDescribeStr.data(),
               mSocket.remote_endpoint().address().to_string().data(),
               mSocket.remote_endpoint().port()));

  mSocket.async_read_some(
        boost::asio::buffer(mBuffer),
        boost::bind(&Connection::handleRead,
                    shared_from_this(),
                    boost::asio::placeholders::error,
                    boost::asio::placeholders::bytes_transferred));

  RETURN;
} // Connection::start

/*******************************************************************************
 *
 *
 *
 ******************************************************************************/
void Connection::close()
{
  ENTER("Connection::close");

  TRACE(3, STR("%s Closing connection.",
               mDescribeStr.data()));

  boost::system::error_code ignored_ec;
  mSocket.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ignored_ec);

  RETURN;
} // Connection::close

/*******************************************************************************
 *
 *
 *
 ******************************************************************************/
void Connection::handleRead(const boost::system::error_code& error,
                            std::size_t                      bytes_transferred)
{
  ENTER("Connection::handleRead");

  if (!error)
  {
    TRACE_BUS_RECEIVE(mBuffer.data(), bytes_transferred,
    		"%s Received data.", mDescribeStr.data());

    requestToQueue_e inStatus;
    Message* pInMessage = new Message(mBuffer.data(),
                                      bytes_transferred,
                                      mInstance);

    inStatus = mRequestHandler.putRequestOnInQueue(pInMessage);
    // From this point on data allocated under pInMessage
    // is handled by MessageQueue.
    // delete pInMessage;
    // pInMessage = 0;

    if (inStatus == REQUEST_IS_ON_QUEUE)
    {
      TRACE(3, STR("%s Request placed on message in queue.",
                   mDescribeStr.data()));

      Message* pOutMessage = 0;
      replyFromQueue_e outStatus;

      outStatus = mRequestHandler.getReplyFromOutQueue(pOutMessage, mInstance);

      if (outStatus == REPLY_QUEUE_IS_EMPTY)
      {
        TRACE(3, STR("%s No messages in queue for this connection.",
                     mDescribeStr.data()));
        mSocket.async_read_some(
              boost::asio::buffer(mBuffer),
              boost::bind(&Connection::handleRead,
                          shared_from_this(),
                          boost::asio::placeholders::error,
                          boost::asio::placeholders::bytes_transferred));
      }
      else if (outStatus == REPLY_PRESENT)
      {
        TRACE(3, STR("%s Send pending message from out queue.",
                     mDescribeStr.data()));

        if (pOutMessage)
        {
          boost::asio::const_buffers_1 outBuff(pOutMessage->getData(),
                                               pOutMessage->getDataSize());
          TRACE_BUS_SEND(
        		  boost::asio::buffer_cast<const void*>(outBuff),
                  boost::asio::buffer_size(outBuff),
                  "%s Sending message Id=0x%08X",
				  mDescribeStr.data(),
				  pOutMessage->getId());
          // remove message
          DELETE_MESSAGE_P(pOutMessage);

          boost::asio::async_write(mSocket,
                                   outBuff,
                                   boost::bind(&Connection::handleWrite,
                                               shared_from_this(),
                                               boost::asio::placeholders::error));
        }
        else
        {
          TRACE_ERROR(STR("%s Dequeued message is NULL.",
                          mDescribeStr.data()));
        }
      }
      else
      {
        TRACE_ERROR(STR("%s Unhandled state=%u of RequestHandler::getReplyFromQueue.",
                        mDescribeStr.data(),
                        outStatus));
      }

    }
    else if (inStatus == REQUEST_INCOMPLETE)
    {
      TRACE(3, STR("%s Request incomplete, reading more data.",
                   mDescribeStr.data()));

      mSocket.async_read_some(boost::asio::buffer(mBuffer),
          boost::bind(&Connection::handleRead, shared_from_this(),
            boost::asio::placeholders::error,
            boost::asio::placeholders::bytes_transferred));
    }
    else // REQUEST_ERROR
    {
      TRACE_ERROR(STR("%s Request error, closing connection",
                      mDescribeStr.data()));
      close();
    }
  }
  else
  {
    TRACE_ERROR(STR("%s Connection failed with error '%s'.",
                    mDescribeStr.data(),
                    error.message().data()));
  }

  RETURN;
}

/*******************************************************************************
 *
 *
 *
 ******************************************************************************/
void Connection::handleWrite(const boost::system::error_code& error)
{
  ENTER("Connection::handleWrite");

  TRACE(3, STR("%s Reply sent.",
               mDescribeStr.data()));
  start();

  RETURN;
}

} // namespace common
} // namespace tsc
